/**
 * Defines the BitField class, that compactly stores multiple values as a short
 * series of bits.
 * @module montage/core/bitfield
 * @requires montage/core/core
 */

var Montage = require("./core").Montage;

/**
 * The BitField object compactly stores multiple values as a short series of
 * bits.
 * This implementation is limited to 32 fields.
 * @class BitField
 * @classdesc Compactly stores multiple values as a short series of bits.
 * @extends Montage
 */
var BitField = exports.BitField = Montage.specialize( /** @lends BitField */ {

    /**
     * Creates a new BitField object containing the fields provided in the
     * propertyDescriptor parameter.
     * @function
     * @param {Object} propertyDescriptor An object containing one or more
     * property name/value pairs.
     * Each pair is added to the new BitField.
     * @returns {Object} A new BitField object that contains fields described
     * by the property descriptor.
     * @example
     * var bitField = new BitField();
     * bitField = new BitField().initWithDescriptor({
     *     likes_golf: {
     *         value: false
     *     },
     *     likes_basketball: {
     *         value: true
     *     },
     *     likes_baseball: {
     *         value: false
     *     },
     * });
     */
    initWithDescriptor: {
        enumerable: false,
        value: function (propertyDescriptor) {
            var fieldName;
            this.reset();
            for (fieldName in propertyDescriptor) {
                if (propertyDescriptor.hasOwnProperty(fieldName)) {
                    this.addField(fieldName, propertyDescriptor[fieldName].value);   
                }
            }
            return this;
        }
    },

    /**
     * Adds a new field to a BitField instance.
     * @function
     * @param {string} aFieldName The name of the field to add.
     * @param {Mixed} defaultValue The new field's default value.
     */
    addField: {
        enumerable: false,
        value: function (aFieldName, defaultValue) {
            if (aFieldName in this) {
                return;
            }
            if (this._fieldCount >= 32) {
                throw "BitField 32 fields limit reached.";
            }
            //We try to recycle slots as limited to 32bits
            this._trueValue += (this._fields[aFieldName] = this._constantsToReuse.length ? this._constantsToReuse.shift() : (1 << this._fieldCount));
            Montage.defineProperty(this, aFieldName, {
                enumerable: true,
                get: function () {
                    return (this._value === this._trueValue);
                },
                set: function (value) {

                    if (value) {
                        this._value |= this._fields[aFieldName];
                    } else {
                        this._value &= ~ (this._fields[aFieldName]);
                    }

                    if (this.value) {
                        this.callDelegateMethod();
                    }
                }
            });
            this._fieldCount++;
            if (!! defaultValue) {
                this[aFieldName] = true;
            }
        }
    },

    _constantsToReuse: {
        enumerable: false,
        value: []
    },

    /**
     * Removes a field from the bitfield.
     * @function
     * @param {string} aFieldName The name of the field to remove.
     */
    removeField: {
        enumerable: false,
        value: function (aFieldName) {
            delete this[aFieldName];
            this._constantsToReuse.push(this._fields[aFieldName]);
            this._trueValue -= this._fields[aFieldName];
            delete this._fields[aFieldName];
        }
    },

    /**
     * The BitField object's delegate.
     * @type {Property}
     * @default null
     */
    delegate: {
        enumerable: false,
        value: null
    },

    /**
     * @function
     * @returns Nothing
     */
    callDelegateMethod: {
        value: function () {
            var delegateMethod;
            if (this.delegate && typeof (delegateMethod = this.delegate.bitFieldDidBecomeTrue) === "function") {
                delegateMethod.call(this.delegate, this);
            }
        },
        enumerable: false
    },
    /**
     * @type {Function}
     * @default {number} 0
     */
    value: {
        enumerable: false,
        get: function () {
            return (this._value === this._trueValue);
        }
    },

    _fieldCount: {
        enumerable: false,
        value: 0
    },

    _value: {
        enumerable: false,
        value: 0
    },


    _trueValue: {
        enumerable: false,
        value: 0
    },

    /**
     * @function
     */
    reset: {
        enumerable: false,
        value: function () {
            this._value = 0x0;
        }
    },

    _fields: {
        enumerable: false,
        value: {}
    },

    /**
     * @function
     */
    toString: {
        value: function () {
            var fieldNames = this._fields,
                i,
                iField,
                result = "";
            for (i = 0; (iField = fieldNames[i]); i++) {
                result += iField + "[" + (this._value & fieldNames[iField]) + "], ";
            }
            return result;
        }
    }

});

